<script setup lang="ts">
import * as THREE from "three";
import { ref, onMounted, type Ref, h } from "vue";
import gsap from "gsap";
import { Room } from "./utils/Room";
import { PositionSprite } from "./utils/PositionSprite";
import { TooltipSprite } from "./utils/TooltipSprite";

// 初始化场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);

// 相机放入到场景中
camera.position.set(0, 0, 0.01);

// 创建渲染器
const renderer = new THREE.WebGLRenderer();
// 设置渲染器大小
renderer.setSize(window.innerWidth, window.innerHeight);

const container = ref<HTMLElement | null>(null);

const render = () => {
  renderer.render(scene, camera);
  requestAnimationFrame(render);
};

window.addEventListener("resize", () => {
  // 更新相机的宽高比
  camera.aspect = window.innerWidth / window.innerHeight;
  // 更新相机的投影矩阵
  camera.updateProjectionMatrix();
  // 更新渲染器的大小
  renderer.setSize(window.innerWidth, window.innerHeight);
});

// show tooltip
const tooltipBox = ref<HTMLElement | null>(null);
const tooltipPosition = ref({
  left: "-100%",
  top: "-100%",
});
const tooltipContent: Ref<Record<string, any>> = ref({
  name: "",
  description: "",
});

function tooltipShow(event: MouseEvent, spriteList: THREE.Sprite[]) {
  const raycaster = new THREE.Raycaster();
  const pointer = new THREE.Vector2();
  event.preventDefault();
  // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
  pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
  pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;

  // 通过摄像机和鼠标位置更新射线
  raycaster.setFromCamera(pointer, camera);
  // 计算物体和射线的交点
  const intersects = raycaster.intersectObjects(spriteList);
  if (intersects.length > 0) {
    if (intersects[0].object.userData.type === "information") {
      const element = event.target as HTMLElement;
      const elementWidth = element.clientWidth / 2;
      const elementHeight = element.clientHeight / 2;
      const worldVector = new THREE.Vector3(
        intersects[0].object.position.x,
        intersects[0].object.position.y,
        intersects[0].object.position.z
      );
      const position = worldVector.project(camera);

      let left = Math.round(
        elementWidth * position.x +
        elementWidth -
        tooltipBox.value!.clientWidth / 2
      );

      let top = Math.round(
        -elementHeight * position.y +
        elementHeight -
        tooltipBox.value!.clientHeight / 2
      );

      tooltipPosition.value = {
        left: `${left}px`,
        top: `${top}px`,
      };

      tooltipContent.value = intersects[0].object.userData;
    }
  }
  else { 
    handleTooltipHide(event);
  }
}

function handleTooltipHide(event: MouseEvent) {
  event.preventDefault();
  tooltipPosition.value = {
    left: "-100%",
    top: "-100%",
  };
  tooltipContent.value = {
    name: "",
    description: "",
  };
}

const spriteList: THREE.Sprite[] = [];

onMounted(() => { 
  if (container.value) {
    container.value.appendChild(renderer.domElement);
    render();

    let isMouseDown = false;
    container.value.addEventListener(
      "mousedown",
      () => {
        isMouseDown = true;
      },
      false
    );
    container.value.addEventListener(
      "mouseup",
      () => {
        isMouseDown = false;
      },
      false
    );
    container.value.addEventListener(
      "mouseout",
      () => {
        isMouseDown = false;
      },
      false
    );

    container.value.addEventListener("mousemove", (event) => {
      if (isMouseDown) {
        camera.rotation.x += event.movementY * 0.01;
        camera.rotation.y += event.movementX * 0.01;
        camera.rotation.order = "YXZ";
      }
    });

    new Room("客厅", "living", "./images/livingRoom/", scene);

    const balconyPosition = new THREE.Vector3(0, 0, -10);
    new Room("阳台", "balcony", "./images/balcony/", scene, balconyPosition);

    // 阳台位置标识
    const balconySprite = new PositionSprite(
      "阳台",
      new THREE.Vector3(0, 0, -4),
      scene,
      camera
    )
    balconySprite.onClick(() => { 
      gsap.to(camera.position, {
        duration: 1,
        x: 0,
        y: 0,
        z: -10,
      });
    })

    // 阳台回到客厅位置标识
    const balconyBackSprite = new PositionSprite(
      "客厅",
      new THREE.Vector3(1, 0, -6),
      scene,
      camera
    )
    balconyBackSprite.onClick(() => { 
      gsap.to(camera.position, {
        duration: 1,
        x: 0,
        y: 0,
        z: 0,
      });
    })

    const kitchenPosition = new THREE.Vector3(2, 0, 10);
    const kitchenEuler = new THREE.Euler(0, -Math.PI / 2, 0);
    new Room("厨房", "kitchen", "./images/kitchen/", scene, kitchenPosition, kitchenEuler);

    // 厨房位置标识
    const kitchenSprite = new PositionSprite(
      "厨房",
      new THREE.Vector3(1.5, 0, 4),
      scene,
      camera
    )
    kitchenSprite.onClick(() => { 
      gsap.to(camera.position, {
        duration: 1,
        x: kitchenPosition.x,
        y: kitchenPosition.y,
        z: kitchenPosition.z,
      });
    })

    // 厨房回到客厅位置标识
    const kitchenBackSprite = new PositionSprite(
      "客厅",
      new THREE.Vector3(1, 0, 6),
      scene,
      camera
    )
    kitchenBackSprite.onClick(() => { 
      gsap.to(camera.position, {
        duration: 1,
        x: 0,
        y: 0,
        z: 0,
      });
    })

    const tooltipSprite1 = new TooltipSprite(
      "./images/dot.png",
      new THREE.Vector3(1.5, -0.1, -3),
      scene,
      camera,
      {
        name: "工艺画",
        description: "十分抽象的工艺画，给人一种很有艺术感的感觉",
        type: "information",
      }
    )

    const tooltipSprite2 = new TooltipSprite(
      "./images/dot.png",
      new THREE.Vector3(-2.5, -0.1, -3),
      scene,
      camera,
      {
        name: "木雕艺术品",
        description: "这是一件木雕艺术品，展现了精湛的工艺和设计",
        type: "information",
      }
    )

    const tooltipSprite3 = new TooltipSprite(
      "./images/dot.png",
      new THREE.Vector3(3, 1, 2),
      scene,
      camera,
      {
        name: "艺术画",
        description: "这是一件艺术化的作品，展现了独特的设计理念",
        type: "information",
      }
    )

    spriteList.push(tooltipSprite1.sprite);
    spriteList.push(tooltipSprite2.sprite);
    spriteList.push(tooltipSprite3.sprite);

    renderer.domElement.addEventListener("mousemove", (e) => { 
      tooltipShow(e, spriteList);
    });
    tooltipBox.value!.addEventListener("mouseleave", handleTooltipHide);
  }
})

</script>

<template>
  <div class="container" ref="container"></div>
  <!-- 信息点弹出层 -->
  <div class="tooltip-box" :style="tooltipPosition" ref="tooltipBox">
    <div class="wrapper">
      <div class="name">标题: {{ tooltipContent.name }}</div>
      <div class="description">说明: {{ tooltipContent.description }}</div>
    </div>
  </div>
</template>

<style lang="scss">
*{
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
.container{
  width: 100vw;
  height: 100vh;
}
.tooltip-box {
  position: absolute;
  padding: 0px 0px 40px 0px;
  line-height: 30px;
  border-radius: 4px;
  color: #fff;
  z-index: 100;
  cursor: pointer;
  .wrapper {
    position: relative;
    width: 240px;
    max-height: 200px;
    padding: 10px;
    background-color: rgba(0, 0, 0, 0.6);
    .name {
      width: 100%;
      padding: 6px 0;
    }
    .description {
      width: 100%;
      max-height: 100px;
      font-size: 14px;
      overflow-y: auto;
    }
  }
}
</style>
